package util.bio.parsers;
import util.bio.annotation.*;
import util.gen.*;
import java.util.*;

public class UCSCGeneLine {
	//fields
	private String displayName = null;
	private String name;
	private String chrom;
	private String strand;
	private String notes;
	private int txStart;
	private int txEnd;
	private int cdsStart;
	private int cdsEnd;
	private ExonIntron[] exons = null;
	private ExonIntron[] introns = null;
	private int tss;
	private float[] scores;
	

	//constructors
	public UCSCGeneLine (String name, Coordinate corr){
		this.name = name;
		chrom = corr.getChromosome();
		txStart = cdsStart = corr.getStart();
		txEnd = cdsEnd = corr.getStop();
		exons = new ExonIntron[1];
		exons[0] = new ExonIntron(txStart, txEnd);
		strand = ".";
	}
	
	public UCSCGeneLine (Bed bed){
		name = bed.getName();
		chrom = bed.getChromosome();
		txStart = cdsStart = bed.getStart();
		txEnd = cdsEnd = bed.getStop();
		exons = new ExonIntron[1];
		exons[0] = new ExonIntron(txStart, txEnd);
		strand = ""+bed.getStrand();
	}

	public UCSCGeneLine (String[] tokens, int numToSubtractFromEnd, boolean displayNamePresent){
		try {
			int i = 0;
			if (displayNamePresent) {
				i = 1;
				displayName = tokens[0];
			}
			name = tokens[0+i];
			chrom = tokens[1+i];
			strand = tokens[2+i];
			txStart = Integer.parseInt(tokens[3+i]);
			txEnd = Integer.parseInt(tokens[4+i]);
			cdsStart = Integer.parseInt(tokens[5+i]);
			cdsEnd = Integer.parseInt(tokens[6+i]);
			txEnd-= numToSubtractFromEnd;
			cdsEnd-= numToSubtractFromEnd;

			//calc tss
			if (strand.equals("-")) tss = txEnd;
			else tss = txStart;

			//make exons
			int exonCount = Integer.parseInt(tokens[7+i]);
			if (exonCount > 0){
				exons = new ExonIntron[exonCount];
				int[] starts = Num.stringArrayToInts(tokens[8+i],",");
				int[] stops = Num.stringArrayToInts(tokens[9+i],",");		
				if (starts.length != exonCount || stops.length != exonCount) {
					Misc.printExit("\nError: problem parsing exons from "+Misc.stringArrayToString(tokens, "\t"));
				}
				for (int j=0; j< exonCount; j++){
					exons[j] = new ExonIntron(starts[j], stops[j]-numToSubtractFromEnd);
				}
				if (exonCount > 1) Arrays.sort(exons);
			}

			//make introns
			if (exonCount > 1){
				//find smallest start and size
				int smallest = exons[0].getStart();
				int size = exons[exons.length-1].getEnd() - smallest;
				//make boolean array to mask
				boolean[] masked = new boolean[size+1];
				//for each exon mask boolean
				for (int k=0; k< exons.length; k++){
					int end = exons[k].getEnd() - smallest;
					int start = exons[k].getStart() - smallest;
					for (int j= start; j< end; j++){
						masked[j] = true;
					}
				}
				//fetch blocks of false
				int[][] startStop = ExportIntergenicRegions.fetchFalseBlocks(masked,0,0);
				//add smallest to starts and ends to return to real coordinates and make introns
				if (startStop.length>0){
					introns = new ExonIntron[startStop.length];
					for (int k=0; k< startStop.length; k++){
						introns[k] = new ExonIntron(startStop[k][0]+smallest, startStop[k][1]+smallest+1);
					}
				}
			}

			//any notes?
			if (tokens.length > 10+i){
				StringBuffer sb = new StringBuffer(tokens[10]);
				for (int j=11+i; j< tokens.length; j++){
					sb.append("\t");
					sb.append(tokens[j]);
				}
				notes = sb.toString();
			}
		} catch (Exception e){
			System.out.println("\nError parsing UCSCGeneLine "+Misc.stringArrayToString(tokens, " "));
			e.printStackTrace();
		}
	}

	public String scoresToString(){
		StringBuilder sb = new StringBuilder();
		if (displayName != null) {
			sb.append(displayName); sb.append("\t");
		}
		sb.append(name); sb.append("\t");
		sb.append(chrom); sb.append("\t");
		sb.append(txStart); sb.append("\t");
		sb.append(txEnd); 
		//scores
		if (scores != null){
			for (int i=0; i< scores.length; i++){
				sb.append("\t");
				sb.append(scores[i]);
			}
		}
		return sb.toString();
	}
	
	public String  coordinates(){
		StringBuilder sb = new StringBuilder();
		sb.append(chrom); sb.append("\t");
		sb.append(strand); sb.append("\t");
		sb.append(txStart); sb.append("\t");
		sb.append(txEnd); 
		return sb.toString();
	}
	
	public String exonsToString(){
		StringBuilder sb = new StringBuilder();
		String chrTab = chrom +"\t";
		for (int i=0; i<exons.length; i++){
			sb.append(chrTab);
			sb.append(exons[i].getStart());
			sb.append("\t");
			sb.append(exons[i].getEnd());
			sb.append("\n");
		}
		return sb.toString();
	}

	public String simpleToString(){
		StringBuffer sb = new StringBuffer();
		if (displayName != null) {
			sb.append(displayName); sb.append("\t");
		}
		sb.append(name); sb.append("\t");
		sb.append(chrom); sb.append("\t");
		sb.append(txStart); sb.append("\t");
		sb.append(txEnd); sb.append("\t");
		sb.append(strand); sb.append("\t");
		sb.append(tss); sb.append("\t");
		return sb.toString();
	}

	public String toString(){
		StringBuffer sb = new StringBuffer();
		if (displayName != null) {
			sb.append(displayName); sb.append("\t");
		}
		sb.append(name); sb.append("\t");
		sb.append(chrom); sb.append("\t");
		sb.append(strand); sb.append("\t");
		sb.append(txStart); sb.append("\t");
		sb.append(txEnd); sb.append("\t");
		sb.append(cdsStart); sb.append("\t");
		sb.append(cdsEnd); sb.append("\t");
		if (exons != null){
			sb.append(exons.length);sb.append("\t");
			sb.append(exons[0].getStart());
			for (int i=1; i<exons.length; i++){
				sb.append(",");
				sb.append(exons[i].getStart());
			}
			sb.append("\t");
			sb.append(exons[0].getEnd());
			for (int i=1; i<exons.length; i++){
				sb.append(",");
				sb.append(exons[i].getEnd());
			}
		}
		return sb.toString();
	}
	
	public String toStringAll(){
		StringBuffer sb = new StringBuffer();
		if (displayName != null) {
			sb.append(displayName); sb.append("\t");
		}
		sb.append(name); sb.append("\t");
		sb.append(chrom); sb.append("\t");
		sb.append(strand); sb.append("\t");
		sb.append(txStart); sb.append("\t");
		sb.append(txEnd); sb.append("\t");
		sb.append(cdsStart); sb.append("\t");
		sb.append(cdsEnd); sb.append("\t");
		if (exons != null){
			sb.append(exons.length);sb.append("\t");
			sb.append(exons[0].getStart());
			for (int i=1; i<exons.length; i++){
				sb.append(",");
				sb.append(exons[i].getStart());
			}
			sb.append("\t");
			sb.append(exons[0].getEnd());
			for (int i=1; i<exons.length; i++){
				sb.append(",");
				sb.append(exons[i].getEnd());
			}
		}
		else sb.append("\t");		
		if (introns != null){
			sb.append(introns.length);sb.append("\t");
			sb.append(introns[0].getStart());
			for (int i=1; i<introns.length; i++){
				sb.append(",");
				sb.append(introns[i].getStart());
			}
			sb.append("\t");
			sb.append(introns[0].getEnd());
			for (int i=1; i<introns.length; i++){
				sb.append(",");
				sb.append(introns[i].getEnd());
			}
		}
		else sb.append("\t");
		
		if (notes != null) {
			sb.append("\t");
			sb.append(notes);
		}
		return sb.toString();
	}

	public int getCdsEnd() {
		return cdsEnd;
	}

	public int getCdsStart() {
		return cdsStart;
	}

	public String getChrom() {
		return chrom;
	}

	public ExonIntron[] getExons() {
		return exons;
	}

	public String getName() {
		return name;
	}

	public String getStrand() {
		return strand;
	}

	public int getTxEnd() {
		return txEnd;
	}

	public int getTxStart() {
		return txStart;
	}

	public String getNotes() {
		return notes;
	}

	public void setNotes(String notes) {
		this.notes = notes;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getTss() {
		return tss;
	}

	public float[] getScores() {
		return scores;
	}

	public void setScores(float[] scores) {
		this.scores = scores;
	}

	public String getDisplayName() {
		return displayName;
	}

	public void setDisplayName(String displayName) {
		this.displayName = displayName;
	}

	public void setCdsEnd(int cdsEnd) {
		this.cdsEnd = cdsEnd;
	}

	public void setCdsStart(int cdsStart) {
		this.cdsStart = cdsStart;
	}

	public void setExons(ExonIntron[] exons) {
		this.exons = exons;
	}

	public void setTss(int tss) {
		this.tss = tss;
	}

	public void setTxEnd(int txEnd) {
		this.txEnd = txEnd;
	}

	public void setTxStart(int txStart) {
		this.txStart = txStart;
	}

	public ExonIntron[] getIntrons() {
		return introns;
	}

	public void setIntrons(ExonIntron[] introns) {
		this.introns = introns;
	}
}
